﻿global using Devonline.Core;
global using static Devonline.Core.AppSettings;
using System.Linq.Expressions;
using System.Reflection;
using Devonline.AspNetCore;
using Devonline.Database.NoSQL;
using Devonline.Entity;
using InfluxDB.Client;
using InfluxDB.Client.Api.Domain;
using InfluxDB.Client.Core;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace Devonline.Database.TimeSeries.InfluxDB;

/// <summary>
/// influxdb 数据库 flux 通用操作服务
/// </summary>
public sealed class InfluxDataService : NoSQLDataService, ITimeSeriesDataService, INoSQLDataService, IDisposable
{
    /// <summary>
    /// InfluxDB Client
    /// </summary>
    private readonly InfluxDBClient _client;
    /// <summary>
    /// 构造方法
    /// </summary>
    /// <param name="logger"></param>
    /// <param name="endpoint"></param>
    public InfluxDataService(
        ILogger<InfluxDataService> logger,
        IDatabaseEndpoint endpoint,
        IHttpContextAccessor httpContextAccessor
        ) : base(logger, endpoint, httpContextAccessor) => _client = new InfluxDBClient(endpoint.Host!, endpoint.Token!);

    /// <summary>
    /// 从数据库查询多行数据
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <typeparam name="TQueryRequest">查询请求表达式类型</typeparam>
    /// <param name="request">查询请求</param>
    /// <returns></returns>
    public override async Task<PagedResult<TModel>> GetPagedResultAsync<TModel>()
    {
        var userName = _httpContext.GetUserName();
        _logger.LogDebug("用户 {userName} 将查询 {host} 的数据库 {database}", userName, _endpoint.Host, _endpoint.Database);
        var request = (TimeRangePagedRequest)_request.GetPagedRequest();
        request.StartTime = _request.GetRequestOption<DateTime>(nameof(request.StartTime));
        request.EndTime = _request.GetRequestOption<DateTime>(nameof(request.EndTime));
        var flux = GetFluxQueryString<TModel>(request);
        _logger.LogDebug($"flux query string is: {flux}");
        var queryApi = _client.GetQueryApi();
        var data = await queryApi.QueryAsync<TModel>(flux, _endpoint.Domain);
        _logger.LogInformation("用户 {userName} 已查询 {host} 的数据库 {database}, 查询结果: " + data.Count, userName, _endpoint.Host, _endpoint.Database);
        return new PagedResult<TModel>
        {
            PageIndex = request.PageIndex,
            PageSize = request.PageSize,
            Total = data.Count,
            Data = data
        };
    }

    /// <summary>
    /// 释放 InfluxDBClient
    /// </summary>
    public void Dispose()
    {
        _client.Dispose();
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// 写入一行数据到数据库
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="model">待写入的数据</param>
    /// <returns></returns>
    protected override async Task InternalAddAsync<TModel>(TModel model) => await _client.GetWriteApiAsync().WriteMeasurementAsync(model, GetWritePrecision(_endpoint.Precision), _endpoint.Database, _endpoint.Domain).ConfigureAwait(false);
    /// <summary>
    /// 写入多行数据到数据库
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="data">待写入的数据</param>
    /// <returns></returns>
    protected override async Task InternalAddsAsync<TModel>(IEnumerable<TModel> data) => await _client.GetWriteApiAsync().WriteMeasurementsAsync(data.ToArray(), GetWritePrecision(_endpoint.Precision), _endpoint.Database, _endpoint.Domain).ConfigureAwait(false);

    /// <summary>
    /// 获取 influx flux 查询表达式
    /// </summary>
    /// <param name="request">带时间条件的分页查询请求</param>
    /// <returns></returns>
    private string GetFluxQueryString<TModel>(TimeRangePagedRequest request) where TModel : class, new()
    {
        ArgumentNullException.ThrowIfNull(_endpoint.Database);
        ArgumentNullException.ThrowIfNull(_endpoint.DataTable);
        var flux = $"from(bucket:\"{_endpoint.Database}\")" + CHAR_NEW_LINE;

        //范围
        var startTime = request.StartTime ?? DateTime.Today;
        flux += $"|> range(start: {startTime.ToString(DEFAULT_ISO_DATETIME_WITH_TIMEZONE_FORMAT)}";
        if (request.EndTime.HasValue)
        {
            flux += $", stop: {request.EndTime.Value.ToString(DEFAULT_ISO_DATETIME_WITH_TIMEZONE_FORMAT)}";
        }

        flux += ")" + CHAR_NEW_LINE;

        //过滤
        var type = typeof(TModel);
        flux += $"|> filter(fn: (r) => r._measurement == \"{_endpoint.DataTable ?? type.GetAttributeValue<Measurement, string>(nameof(Measurement.Name))}\")" + CHAR_NEW_LINE;

        var groups = new List<string>();
        var propertyInfos = request.GetType().GetProperties().Where(x => x.CanRead && x.CanWrite);
        if (propertyInfos.Any())
        {
            foreach (var propertyInfo in propertyInfos)
            {
                var column = type.GetProperty(propertyInfo.Name)?.GetCustomAttribute<Column>();
                var value = propertyInfo.GetValue(request)?.ToString();
                if (column != null && !string.IsNullOrWhiteSpace(value))
                {
                    flux += GetFluxFieldQueryString(column.Name, value, column.IsTag);
                    if (column.IsTag)
                    {
                        groups.Add($"\"{column.Name}\"");
                    }
                }
            }
        }

        //flux += "|> toString()" + CHAR_NEW_LINE;

        ////分组
        //if (groups.Count > 0)
        //{
        //    flux += $"|> group(columns: [\"_measurement\", {groups.ToString(", ")}])" + CHAR_NEW_LINE;
        //}

        //排序
        if (string.IsNullOrWhiteSpace(request.Orderby))
        {
            flux += "|> sort(columns: [\"_time\"], desc: true)" + CHAR_NEW_LINE;
        }
        else
        {
            var orderBy = request.Orderby;
            var desc = request.Orderby.EndsWith(" desc");
            if (desc)
            {
                orderBy = orderBy[0..^5];
            }

            var sorts = orderBy.Split(CHAR_COMMA, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
            flux += $"|> sort(column: [\"{sorts.ToString(", ")}\"])" + CHAR_NEW_LINE;
        }

        //分页
        if (request.PageIndex < UNIT_ONE)
        {
            request.PageIndex = DEFAULT_PAGE_INDEX;
        }

        if (request.PageSize < UNIT_ONE)
        {
            request.PageSize = DEFAULT_PAGE_SIZE;
        }

        flux += $"|> limit(n: {request.PageSize}, offset: {(request.PageIndex - 1) * request.PageSize})";
        return flux;
    }
    /// <summary>
    /// 单个 influx model field 构造的查询表达式
    /// </summary>
    /// <param name="field"></param>
    /// <param name="value"></param>
    /// <param name="isTag"></param>
    /// <returns></returns>
    private static string GetFluxFieldQueryString(string field, string? value = default, bool isTag = true)
    {
        var result = string.Empty;
        if (!string.IsNullOrWhiteSpace(value))
        {
            var filters = new List<string>();
            var values = value.Split(CHAR_COMMA, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
            foreach (var fieldValue in values)
            {
                filters.Add(isTag ? $" r.{field} == \"{fieldValue}\"" : $" r._field == \"{field}\" and r._value == \"{fieldValue}\"");
            }

            if (filters.Count > 0)
            {
                result = $"|> filter(fn: (r) => {filters.ToString(" or ")})" + CHAR_NEW_LINE;
            }
        }

        return result;
    }
    /// <summary>
    /// 获取时间精度
    /// </summary>
    /// <param name="timeKind"></param>
    /// <returns></returns>
    private static WritePrecision GetWritePrecision(TimeKind timeKind) => timeKind switch
    {
        TimeKind.Second => WritePrecision.S,
        TimeKind.MilliSecond => WritePrecision.Ms,
        TimeKind.MicroSecond => WritePrecision.Us,
        _ => WritePrecision.Ns
    };

    public override Task DeleteAsync<TModel, TKey>(TKey id, DataServiceContext? context = null)
    {
        throw new NotImplementedException();
    }

    public override Task DeletesAsync<TModel, TKey>(IEnumerable<TKey> ids, DataServiceContext? context = null)
    {
        throw new NotImplementedException();
    }

    public override Task<IQueryable<TModel>> GetQueryableAsync<TModel>()
    {
        throw new NotImplementedException();
    }

    public override Task<IQueryable<TModel>> GetQueryableAsync<TModel>(Expression<Func<TModel, bool>> predicate)
    {
        throw new NotImplementedException();
    }

    public override Task<TModel?> FirstOrDefaultAsync<TModel>(Expression<Func<TModel, bool>> predicate) where TModel : class
    {
        throw new NotImplementedException();
    }

    public override Task<PagedResult<TModel>> GetPagedResultAsync<TModel>(Expression<Func<TModel, bool>> predicate)
    {
        throw new NotImplementedException();
    }

    protected override Task InternalUpdateAsync<TModel>(TModel model)
    {
        throw new NotImplementedException();
    }

    protected override Task InternalUpdatesAsync<TModel>(IEnumerable<TModel> data)
    {
        throw new NotImplementedException();
    }
}
